#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
/*
 * Copyright © 2014, Finium Sdn Bhd, All Rights Reserved
 * 
 * HibernateHQLUtil.java
 * Modification History
 * *************************************************************
 * Date				Author						Comment
 * Feb 10, 2012		Venkaiah Chowdary Koneru	Created
 * Mar 27, 2012		Venkaiah Chowdary Koneru	Added support for named queries
 * Oct 26, 2013		Venkaiah Chowdary Koneru	renamed and abstracted functionality
 * *************************************************************
 */
package ${package}.support.hibernate;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import ${package}.dto.ListProp;
import ${package}.support.CommonsUtil;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.util.StringUtils;

/**
 * Utility class to hold HQL query functionality.
 * 
 * @author Venkaiah Chowdary Koneru
 */
public final class HibernateHQLUtil extends AbstractHibernateQueryUtil
{
    /**
     * Helper method to ease the query process.
     * 
     * @param sessionFactory
     *            must not be null.
     * @param countQueryString
     *            will be used only when queryProp.pagination object is not
     *            null. "select count(*)" will be used if this string is blank.
     * @param selectAliasAndPropertyMap
     *            alias and the associated property name map for query Select
     *            section and also Sorting purposes.
     * @param queryStringWithoutSelect
     *            must not be blank. Must not contain query Select section also.
     * @param parameters
     *            can be null if no parameter is required.
     * @param listProp
     *            can be null.
     * @param resultClass
     *            can be null.
     * 
     * @return List of the result object. Total records count will be returned
     *         in the queryProp.pagination.totalRecords property.
     */
    public static List<?> list(SessionFactory sessionFactory,
            final String countQueryString,
            final Map<String, String> selectAliasAndPropertyMap,
            final String queryStringWithoutSelect,
            final Map<String, Object> parameters, final ListProp listProp,
            final Class<?> resultClass)
    {
        return list(sessionFactory, countQueryString,
                generateSelect(selectAliasAndPropertyMap),
                queryStringWithoutSelect, selectAliasAndPropertyMap,
                parameters, listProp, resultClass, false);
    }

    /**
     * Helper method to ease the query process.
     * 
     * @param sessionFactory
     *            must not be null.
     * @param countQueryString
     *            will be used only when queryProp.pagination object is not
     *            null. "select count(*)" will be used if this string is blank.
     * @param selectAliasAndPropertyMap
     *            alias and the associated property name map for query Select
     *            section and also Sorting purposes.
     * @param queryStringWithoutSelect
     *            must not be blank. Must not contain query Select section also.
     * @param parameters
     *            can be null if no parameter is required.
     * @param listProp
     *            can be null.
     * @param resultClass
     *            can be null.
     * @param distinct
     *            to include the distinct keyword in select query
     * @return List of the result object. Total records count will be returned
     *         in the queryProp.pagination.totalRecords property.
     */
    public static List<?> list(SessionFactory sessionFactory,
            final String countQueryString,
            final Map<String, String> selectAliasAndPropertyMap,
            final String queryStringWithoutSelect,
            final Map<String, Object> parameters, final ListProp listProp,
            final Class<?> resultClass, boolean distinct)
    {

        return list(sessionFactory, countQueryString,
                generateSelect(selectAliasAndPropertyMap),
                queryStringWithoutSelect, selectAliasAndPropertyMap,
                parameters, listProp, resultClass, distinct);
    }

    /**
     * Helper method to ease the query process.
     * 
     * @param sessionFactory
     *            must not be null.
     * @param countQueryString
     *            will be used only when queryProp.pagination object is not
     *            null. "select count(*)" will be used if this string is blank.
     * @param selectQueryString
     *            can be blank. Just the select section, for example:
     *            "select a as a, b as b, ...".
     * @param queryStringWithoutSelect
     *            must not be blank. Must not contain query Select section also.
     * @param aliasAndPropertyMap
     *            alias and the associated property name map for Sorting
     *            purposes. Can be null if no sorting required.
     * @param parameters
     *            can be null if no parameter is required.
     * @param resultClass
     *            can be null.
     * @param listProp
     *            can be null.
     * @param distinct
     *            to include the distinct keyword in select query
     * @return List of the result object. Total records count will be returned
     *         in the queryProp.pagination.totalRecords property.
     */
    public static List<?> list(SessionFactory sessionFactory,
            final String countQueryString, final String selectQueryString,
            final String queryStringWithoutSelect,
            final Map<String, String> aliasAndPropertyMap,
            final Map<String, Object> parameters, final ListProp listProp,
            final Class<?> resultClass, boolean distinct)
    {
        if (sessionFactory == null
                || !StringUtils.hasText(queryStringWithoutSelect))
        {
            return null;
        }

        Session session = sessionFactory.getCurrentSession();

        String select = "select ";
        if (distinct)
        {
            select = "select distinct ";
        }

        String groupByQueryString = "";

        String finalCountQueryString = countQueryString;
        if (!StringUtils.hasText(countQueryString))
        {
            finalCountQueryString = "select count(*) ";
        }

        setCountData(
                session.createQuery(finalCountQueryString
                        + queryStringWithoutSelect + groupByQueryString),
                listProp, parameters);

        String sortingString = generateSorting(listProp, aliasAndPropertyMap);

        Query query = session
                .createQuery(select
                        + (selectQueryString == null ? "" : selectQueryString)
                        + queryStringWithoutSelect + groupByQueryString
                        + sortingString);

        if (resultClass != null)
        {
            query.setResultTransformer(Transformers.aliasToBean(resultClass));
        }

        setParameters(parameters, query);

        setPagination(listProp, query);
        return query.list();
    }

    /**
     * Group result by list
     * 
     * @param sessionFactory
     *            must not be null.
     * @param countQueryString
     *            must not be blank. Must be the Named count query string
     * @param selectAliasAndPropertyMap
     *            alias and the associated property name map for query Select
     *            section and also Sorting purposes.
     * @param groupByAliasAndPropertyMap
     *            alias and the associated property name map for group by Select
     *            section and also Sorting purposes.
     * @param queryStringWithoutSelect
     *            must not be blank. Must not contain query Select section also.
     * @param parameters
     *            can be null if no parameter is required.
     * @param listProp
     *            can be null
     * @param resultClass
     *            can be null.
     * @return List of the result object.
     */
    public static List<?> groupByList(SessionFactory sessionFactory,
            final String countQueryString,
            final Map<String, String> selectAliasAndPropertyMap,
            final Map<String, String> groupByAliasAndPropertyMap,
            final String queryStringWithoutSelect,
            final Map<String, Object> parameters, final ListProp listProp,
            final Class<?> resultClass)
    {

        return groupByList(sessionFactory, countQueryString,
                generateSelect(selectAliasAndPropertyMap), " group by "
                        + generateSelectForGroupBy(groupByAliasAndPropertyMap),
                queryStringWithoutSelect, selectAliasAndPropertyMap,
                parameters, listProp, resultClass);
    }

    /**
     * Group query result
     * 
     * @param sessionFactory
     *            must not be null.
     * @param countQueryString
     *            will be used only when queryProp.pagination object is not
     *            null. "select count(*)" will be used if this string is blank.
     * @param selectQueryString
     *            can be blank. Just the select section, for example:
     *            "select a as a, b as b, ...".
     * @param groupByQueryString
     *            can be blank. just the group by clause of the query.
     * @param queryStringWithoutSelect
     *            must not be blank. Must not contain query Select section also.
     * @param aliasAndPropertyMap
     *            alias and the associated property name map for Sorting
     *            purposes. Can be null if no sorting required.
     * @param parameters
     *            can be null if no parameter is required.
     * @param resultClass
     *            can be null.
     * @param listProp
     *            can be null
     * 
     * @return List of the result object.
     */
    public static List<?> groupByList(SessionFactory sessionFactory,
            final String countQueryString, final String selectQueryString,
            final String groupByQueryString,
            final String queryStringWithoutSelect,
            final Map<String, String> aliasAndPropertyMap,
            final Map<String, Object> parameters, final ListProp listProp,
            final Class<?> resultClass)
    {
        if (sessionFactory == null
                || !StringUtils.hasText(queryStringWithoutSelect))
        {
            return null;
        }

        Session session = sessionFactory.getCurrentSession();

        String select = "select ";

        String finalCountQueryString = countQueryString;
        if (countQueryString == null || countQueryString.trim().length() == 0)
        {
            finalCountQueryString = "select count(*) ";
        }

        setCountData(
                session.createQuery(finalCountQueryString
                        + queryStringWithoutSelect + groupByQueryString),
                listProp, parameters);

        String sortingString = generateSorting(listProp, aliasAndPropertyMap);

        Query query = session
                .createQuery(select
                        + (selectQueryString == null ? "" : selectQueryString)
                        + queryStringWithoutSelect + groupByQueryString
                        + sortingString);

        if (resultClass != null)
        {
            query.setResultTransformer(Transformers.aliasToBean(resultClass));
        }

        setParameters(parameters, query);

        setPagination(listProp, query);

        return query.list();
    }

    /**
     * Append the select part of the hql based on the property and alias map.
     * 
     * @param aliasAndPropertyMap
     *            alias and the associated property name map.
     * 
     * @return String after append the property and alias delimited by comma.
     */
    private static String generateSelect(Map<String, String> aliasAndPropertyMap)
    {
        if (CommonsUtil.isEmpty(aliasAndPropertyMap))
        {
            return "";
        }

        StringBuffer sb = new StringBuffer();
        sb.append(" ");
        for (Entry<String, String> propertyAndAlias : aliasAndPropertyMap
                .entrySet())
        {
            sb.append(propertyAndAlias.getValue());
            sb.append(" as ");
            sb.append(propertyAndAlias.getKey());
            sb.append(",");
        }

        sb.deleteCharAt(sb.length() - 1);
        sb.append(" ");

        return sb.toString();
    }

    /**
     * to generate the group by clause
     * 
     * @param aliasAndPropertyMap
     * @return string representation of groupby query
     */
    private static String generateSelectForGroupBy(
            Map<String, String> aliasAndPropertyMap)
    {
        if (CommonsUtil.isEmpty(aliasAndPropertyMap))
        {
            return "";
        }

        StringBuffer sb = new StringBuffer();
        sb.append(" ");
        for (Entry<String, String> propertyAndAlias : aliasAndPropertyMap
                .entrySet())
        {
            sb.append(propertyAndAlias.getValue());

            sb.append(",");
        }

        sb.deleteCharAt(sb.length() - 1);
        sb.append(" ");

        return sb.toString();
    }

    /**
     * Executes the Count query and sets the total records property value of
     * list prop.
     * 
     * @param countQuery
     *            count query. this should not be empty.
     * @param listProp
     *            list prop object
     * @param parameters
     *            parameters
     */
    private static void setCountData(Query countQuery, ListProp listProp,
            Map<String, Object> parameters)
    {
        if (countQuery != null && listProp != null
                && listProp.getStartIndex() >= getStartIndex())
        {
            setParameters(parameters, countQuery);

            List<?> countList = countQuery.list();
            if (CommonsUtil.isEmpty(countList))
            {
                listProp.setTotalRecords(0);
            }
            else
            {
                listProp.setTotalRecords(((Long) countList.iterator().next())
                        .longValue());
            }
        }
    }

    /**
     * Constructor. private to prevent unnecessary instantiation.
     */
    private HibernateHQLUtil()
    {
    }
}
